<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Loader Class
 *
 * Loader组件在CI里面也是一个很重要的组件，功能也比较明了。
 * 如果已经阅读过Controller组件，会发现Controller组件的代码也只有十来行，但它却可以做很多事，一定程度上
 * 要归功于Loader组件这个好助手或者好基友。
 * 不过Loader组件的代码真的不少，主要以常用的几个方法以主线来探讨：model(),view(),library(),helper();
 */
class CI_Loader {

// All these are set automatically. Don't mess with them.
    /**
     * Nesting level of the output buffering mechanism
     */
    protected $_ci_ob_level;
    /**
     * List of paths to load views from
     */
    protected $_ci_view_paths = array();
    /**
     * List of paths to load libraries from
     */
    protected $_ci_library_paths = array();
    /**
     * List of paths to load models from
     */
    protected $_ci_model_paths = array();
    /**
     * List of paths to load helpers from
     */
    protected $_ci_helper_paths = array();
    /**
     * List of loaded base classes
     */
    protected $_base_classes = array(); // Set by the controller class
    /**
     * List of cached variables
     */
    protected $_ci_cached_vars = array();
    /**
     * List of loaded classes
     */
    protected $_ci_classes = array();
    /**
     * List of loaded files
     */
    protected $_ci_loaded_files = array();
    /**
     * List of loaded models
     */
    protected $_ci_models = array();
    /**
     * List of loaded helpers
     */
    protected $_ci_helpers = array();
    /**
     * List of class name mappings
     */
    protected $_ci_varmap = array('unit_test' => 'unit',
        'user_agent' => 'agent');

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->_ci_ob_level = ob_get_level();
        $this->_ci_library_paths = array(APPPATH, BASEPATH);
        $this->_ci_helper_paths = array(APPPATH, BASEPATH);
        $this->_ci_model_paths = array(APPPATH);
        $this->_ci_view_paths = array(APPPATH.'views/' => TRUE);

        log_message('debug', "Loader Class Initialized");
    }

// --------------------------------------------------------------------

    /**
     * Initialize the Loader
     */
    public function initialize()
    {
        $this->_ci_classes = array();
        $this->_ci_loaded_files = array();
        $this->_ci_models = array();

//这个is_loaded方法就是在core/Common.php中定义的全局函数，在Loader组件还没有加载之前，由它来负责记录
//哪些核心类已经加载过，现在Loader组件要加载了，就把信息交给Loader组件，保存在Loader::$_base_classes中。
        $this->_base_classes =& is_loaded();

//自动加载，加载项是你在config/autoload.php中设置的。
        $this->_ci_autoloader();

        return $this;
    }

// --------------------------------------------------------------------

    /**
     * Is Loaded
     */
    public function is_loaded($class)
    {
        if (isset($this->_ci_classes[$class]))
        {
            return $this->_ci_classes[$class];
        }

        return FALSE;
    }

// --------------------------------------------------------------------

    /**
     * Class Loader
     * $library为相应的类名，$params为实例化此类的时候可能要用到的参数，$object_name为给这个类的实例自义定一个名字。
     */
    public function library($library = '', $params = NULL, $object_name = NULL)
    {
//如果是通过数组加载多个，把它拆开再调用本方法，其实它可以递归调用多维数组，不过没有这个必要。
        if (is_array($library))
        {
            foreach ($library as $class)
            {
                $this->library($class, $params);
            }

            return;
        }

//接下来两个if都是关于合法性的判断。
        if ($library == '' OR isset($this->_base_classes[$library]))
        {
            return FALSE;
        }

        if ( ! is_null($params) && ! is_array($params))
        {
            $params = NULL;
        }

//真正把类加载进来的是下面这个方法。
        $this->_ci_load_class($library, $params, $object_name);
    }

// --------------------------------------------------------------------

    /**
     * Model Loader
     */
    public function model($model, $name = '', $db_conn = FALSE)
    {
//可以以数组形式同时加载多个$model
        if (is_array($model))
        {
            foreach ($model as $babe)
            {
                $this->model($babe);
            }
            return;
        }

        if ($model == '')
        {
            return;
        }

        $path = '';

//判断是否包含目录信息
        if (($last_slash = strrpos($model, '/')) !== FALSE)
        {
            $path = substr($model, 0, $last_slash + 1);

            $model = substr($model, $last_slash + 1);
        }

//如果没有给当前model定义名字，则以$model本身作为名字。
        if ($name == '')
        {
            $name = $model;
        }

//如果已经加载过此model，直接退出本函数。
        if (in_array($name, $this->_ci_models, TRUE))
        {
            return;
        }

        $CI =& get_instance();
//如果加载的model名字与之前加载过的类有冲突，则报错。
        if (isset($CI->$name))
        {
            show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
        }

//model文件必段是全小写。
        $model = strtolower($model);

        foreach ($this->_ci_model_paths as $mod_path)
        {
            if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
            {
                continue;
            }

//如果要求同时连接数据库。则调用Loader::database()方法加载数据库类。
            if ($db_conn !== FALSE AND ! class_exists('CI_DB'))
            {
                if ($db_conn === TRUE)
                {
                    $db_conn = '';
                }

                $CI->load->database($db_conn, FALSE, TRUE);
            }

//加载父类model。
            if ( ! class_exists('CI_Model'))
            {
                load_class('Model', 'core');
            }

//引入当前model
            require_once($mod_path.'models/'.$path.$model.'.php');

//把文件名的第一个字母大写作为类名，规定的命名规范。
            $model = ucfirst($model);

            $CI->$name = new $model();

//保存在Loader::_ci_models中，以后可以用它来判断某个model是否已经加载过。
            $this->_ci_models[] = $name;
            return;
        }

        show_error('Unable to locate the model you have specified: '.$model);
    }

// --------------------------------------------------------------------

    /**
     * Database Loader
     */
    public function database($params = '', $return = FALSE, $active_record = NULL)
    {
        $CI =& get_instance();
        if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db))
        {
            return FALSE;
        }

        require_once(BASEPATH.'database/DB.php');

        if ($return === TRUE)
        {
            return DB($params, $active_record);
        }
        $CI->db = '';

        $CI->db =& DB($params, $active_record);
    }

// --------------------------------------------------------------------

    /**
     * Load the Utilities Class
     */
    public function dbutil()
    {
        if ( ! class_exists('CI_DB'))
        {
            $this->database();
        }

        $CI =& get_instance();

// for backwards compatibility, load dbforge so we can extend dbutils off it
// this use is deprecated and strongly discouraged
        $CI->load->dbforge();

        require_once(BASEPATH.'database/DB_utility.php');
        require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_utility.php');
        $class = 'CI_DB_'.$CI->db->dbdriver.'_utility';

        $CI->dbutil = new $class();
    }

// --------------------------------------------------------------------

    /**
     * Load the Database Forge Class
     */
    public function dbforge()
    {
        if ( ! class_exists('CI_DB'))
        {
            $this->database();
        }

        $CI =& get_instance();

        require_once(BASEPATH.'database/DB_forge.php');
        require_once(BASEPATH.'database/drivers/'.$CI->db->dbdriver.'/'.$CI->db->dbdriver.'_forge.php');
        $class = 'CI_DB_'.$CI->db->dbdriver.'_forge';

        $CI->dbforge = new $class();
    }

// --------------------------------------------------------------------

    /**
     * Load View
     *
     * Loader::view();方法可以和Loader::file()方法一并来阅读，实质上它们都是调用了Loader::_ci_load();方法。
     */
    public function view($view, $vars = array(), $return = FALSE)
    {
        return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
    }

// --------------------------------------------------------------------

    /**
     * Load File
     */
    public function file($path, $return = FALSE)
    {
        return $this->_ci_load(array('_ci_path' => $path, '_ci_return' => $return));
    }

// --------------------------------------------------------------------

    /**
     * Set Variables
     */
    public function vars($vars = array(), $val = '')
    {
        if ($val != '' AND is_string($vars))
        {
            $vars = array($vars => $val);
        }

        $vars = $this->_ci_object_to_array($vars);

        if (is_array($vars) AND count($vars) > 0)
        {
            foreach ($vars as $key => $val)
            {
                $this->_ci_cached_vars[$key] = $val;
            }
        }
    }

// --------------------------------------------------------------------

    /**
     * Get Variable
     */
    public function get_var($key)
    {
        return isset($this->_ci_cached_vars[$key]) ? $this->_ci_cached_vars[$key] : NULL;
    }

// --------------------------------------------------------------------

    /**
     * Load Helper
     */
    public function helper($helpers = array())
    {
//Loader::_ci_prep_filename()方法只是处理文件名，以返回正确的数组而已。
//默认helper的文件名是以_helper为后缀，所以参数可以不用写_helper后缀，当然写也不会出错，因为
//Loader::_ci_prep_filename()会帮你处理掉。
        foreach ($this->_ci_prep_filename($helpers, '_helper') as $helper)
        {
//如果已经加载过此helper，则跳过。
            if (isset($this->_ci_helpers[$helper]))
            {
                continue;
            }

//helper的扩展。并非只有类可以扩展，函数也提供了扩展。注意这里是在APPPATH下。
            $ext_helper = APPPATH.'helpers/'.config_item('subclass_prefix').$helper.'.php';
//如果存在对些helper的扩展。
            if (file_exists($ext_helper))
            {
//先引入CI自带的helper，再引入我们写的扩展。
                $base_helper = BASEPATH.'helpers/'.$helper.'.php';

                if ( ! file_exists($base_helper))
                {
                    show_error('Unable to load the requested file: helpers/'.$helper.'.php');
                }

                include_once($ext_helper);
                include_once($base_helper);

                $this->_ci_helpers[$helper] = TRUE;
                log_message('debug', 'Helper loaded: '.$helper);
                continue;//继续下一个helper的引入。
            }

//如果没有扩展的话，则分别从APPPATH和BASEPATH，即应用目录和系统目录下找到相应的helper。
//Loader::_ci_helper_paths默认是APPPATH和BASEPATH两个目录。如果你要再添加新的目录路径，可以
//通过Loader::add_package_path()方法设置（model,library等同理）
            foreach ($this->_ci_helper_paths as $path)
            {
                if (file_exists($path.'helpers/'.$helper.'.php'))
                {
                    include_once($path.'helpers/'.$helper.'.php');

                    $this->_ci_helpers[$helper] = TRUE;
                    log_message('debug', 'Helper loaded: '.$helper);
                    break;
                }
            }

            if ( ! isset($this->_ci_helpers[$helper]))
            {
                show_error('Unable to load the requested file: helpers/'.$helper.'.php');
            }
        }
    }

// --------------------------------------------------------------------

    /**
     * Load Helpers
     */
    public function helpers($helpers = array())
    {
        $this->helper($helpers);
    }

// --------------------------------------------------------------------

    /**
     * Loads a language file
     */
    public function language($file = array(), $lang = '')
    {
        $CI =& get_instance();

        if ( ! is_array($file))
        {
            $file = array($file);
        }

        foreach ($file as $langfile)
        {
            $CI->lang->load($langfile, $lang);
        }
    }

// --------------------------------------------------------------------

    /**
     * Loads a config file
     */
//这里的config方法，实质是完完全全调用Config组件的load方法而已。
    public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
    {
        $CI =& get_instance();
        $CI->config->load($file, $use_sections, $fail_gracefully);
    }

// --------------------------------------------------------------------

    /**
     * Driver
     */
    public function driver($library = '', $params = NULL, $object_name = NULL)
    {
        if ( ! class_exists('CI_Driver_Library'))
        {
            require BASEPATH.'libraries/Driver.php';
        }

        if ($library == '')
        {
            return FALSE;
        }

        if ( ! strpos($library, '/'))
        {
            $library = ucfirst($library).'/'.$library;
        }

        return $this->library($library, $params, $object_name);
    }

// --------------------------------------------------------------------

    /**
     * Add Package Path
     */
    public function add_package_path($path, $view_cascade=TRUE)
    {
        $path = rtrim($path, '/').'/';

        array_unshift($this->_ci_library_paths, $path);
        array_unshift($this->_ci_model_paths, $path);
        array_unshift($this->_ci_helper_paths, $path);

        $this->_ci_view_paths = array($path.'views/' => $view_cascade) + $this->_ci_view_paths;

        $config =& $this->_ci_get_component('config');
        array_unshift($config->_config_paths, $path);
    }

// --------------------------------------------------------------------

    /**
     * Get Package Paths
     *
     */
    public function get_package_paths($include_base = FALSE)
    {
        return $include_base === TRUE ? $this->_ci_library_paths : $this->_ci_model_paths;
    }

// --------------------------------------------------------------------

    /**
     * Remove Package Path
     */
    public function remove_package_path($path = '', $remove_config_path = TRUE)
    {
        $config =& $this->_ci_get_component('config');

        if ($path == '')
        {
            $void = array_shift($this->_ci_library_paths);
            $void = array_shift($this->_ci_model_paths);
            $void = array_shift($this->_ci_helper_paths);
            $void = array_shift($this->_ci_view_paths);
            $void = array_shift($config->_config_paths);
        }
        else
        {
            $path = rtrim($path, '/').'/';
            foreach (array('_ci_library_paths', '_ci_model_paths', '_ci_helper_paths') as $var)
            {
                if (($key = array_search($path, $this->{$var})) !== FALSE)
                {
                    unset($this->{$var}[$key]);
                }
            }

            if (isset($this->_ci_view_paths[$path.'views/']))
            {
                unset($this->_ci_view_paths[$path.'views/']);
            }

            if (($key = array_search($path, $config->_config_paths)) !== FALSE)
            {
                unset($config->_config_paths[$key]);
            }
        }

        $this->_ci_library_paths = array_unique(array_merge($this->_ci_library_paths, array(APPPATH, BASEPATH)));
        $this->_ci_helper_paths = array_unique(array_merge($this->_ci_helper_paths, array(APPPATH, BASEPATH)));
        $this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
        $this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
        $config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
    }

// --------------------------------------------------------------------

    /**
     * Loader
     */
    protected function _ci_load($_ci_data)
    {
//这里相当于把数组里面的元素拆开成变量。
        foreach (array('_ci_view', '_ci_vars', '_ci_path', '_ci_return') as $_ci_val)
        {
            $$_ci_val = ( ! isset($_ci_data[$_ci_val])) ? FALSE : $_ci_data[$_ci_val];
        }

        $file_exists = FALSE;

//当Loader::_ci_load()方法是通过Loader::file()调用的时候，则会有$_ci_path的值，如果是
//如果Loader::view()调用的话，则有$_ci_view的值。
//如果$_ci_path不为空，则说明当前要加载普通文件。
        if ($_ci_path != '')
        {
//普通文件。这里只是获得文件名，以便找不到报错时候只报文件名而已。
            $_ci_x = explode('/', $_ci_path);
            $_ci_file = end($_ci_x);
        }
        else
        {
//视图文件。
//下面两行操作也是为了让外部可以通过xxx.php或者直接xxx的方式进行传参而已。
            $_ci_ext = pathinfo($_ci_view, PATHINFO_EXTENSION);
            $_ci_file = ($_ci_ext == '') ? $_ci_view.'.php' : $_ci_view;

            foreach ($this->_ci_view_paths as $view_file => $cascade)
            {
//从Loader::$_ci_view_paths中遍历视图文件，如果找到则退出。
//（默认仅有APPPATH/view/下，当然也可以通过Loader::add_package()方法设置）
                if (file_exists($view_file.$_ci_file))
                {
                    $_ci_path = $view_file.$_ci_file;
                    $file_exists = TRUE;
                    break;
                }

//如果没有找到，会根据这个$cascade判断允不允许继续往下一个路径寻找视图文件。
                if ( ! $cascade)
                {
                    break;
                }
            }
        }

//如果找不到文件（普通或视图都一样），则报错。
        if ( ! $file_exists && ! file_exists($_ci_path))
        {
            show_error('Unable to load the requested file: '.$_ci_file);
        }

//下面这个也很关键，其实视图文件里面的代码都是在属于Loader组件的，什么意思？
//你可以随便写一个视图文件，然后在里面写上var_dump($this);可以发现，这个$this，是指Loader。
//为什么会这样子呢？再往下面十几行代码的地方就说明了这一点。
//这里是把CI所有的属性都开放给Loader组件用，这样在视图文件里面就可以通过$this->xxx的方式调用控制器
//所有的东西。

        $_ci_CI =& get_instance();
        foreach (get_object_vars($_ci_CI) as $_ci_key => $_ci_var)
        {
            if ( ! isset($this->$_ci_key))
            {
                $this->$_ci_key =& $_ci_CI->$_ci_key;
            }
        }

//在这里把在控制器里面通过$this->load->view("xxx",$data);中的$data解开，这就是为什么可以在视图文件
//中可以用$data里面的变量的原因。其实还可以通过Loader::vars()方法，设置这些变量，它们会首先保存在
//Loader::$_ci_cached_vars中
        if (is_array($_ci_vars))
        {
            $this->_ci_cached_vars = array_merge($this->_ci_cached_vars, $_ci_vars);
        }
        extract($this->_ci_cached_vars);

//我们在控制器中调用$this->load->view()方法，实质视图并没有马上输出来，而是先将它放到缓冲区。
        ob_start();

//就是这个地方，下面if中有一句eval(xxxx)以及else中有include;而里面的xxxx正是我们要加载的视图文件，
//所以这就是为什么在视图文件里，var_dump($this)，会告诉你当前这个$this是Loader组件，因为视图的代码都是相当于
//嵌入这个地方。
        if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
        {
            echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));//'
        }
        else
        {
            include($_ci_path);
        }

//经过上面的代码，我们的视图文件的内容已经放到了缓冲区了。

        log_message('debug', 'File loaded: '.$_ci_path);

//一般情况下，$_ci_return都为FLASE，即不要求通过$this->load->view()返回输出内容，而是直接放到缓冲区静候处理;
//当然你也可以先拿出数据，在控制器里面处理一下，再输出，例如在控制器中
//$output=$this->load->view("x",$data,TRUE);，当为TRUE的时候，下面的代码就起作用了。
        if ($_ci_return === TRUE)
        {
            $buffer = ob_get_contents();
            @ob_end_clean();
            return $buffer;
        }

//下面这个很关键，因为有可能当前这个视图文件是被另一个视图文件通过$this->view()方法引入，即视图文件嵌入视图文件
//从而导致多了一层缓冲。
//为了保证缓冲内容最后交给Output处理时，缓冲级别只比Loader组件加载时多1（这个1就是最父层的视图文件引起的）
//这里必须先flush掉当前层视图引起的这次缓冲，以保证Output正常工作。
        if (ob_get_level() > $this->_ci_ob_level + 1)
        {
            ob_end_flush();
        }
        else
        {
//如果不是多1，则说明当前引入的视图文件就是直接在控制器里面引入的那个，而不是由某个视图文件再引入的。

//把缓冲区的内容交给Output组件并清空关闭缓冲区。
            $_ci_CI->output->append_output(ob_get_contents());
            @ob_end_clean();
        }
    }

// --------------------------------------------------------------------

    /**
     * Load class
     */
    protected function _ci_load_class($class, $params = NULL, $object_name = NULL)
    {
//去掉后缀.php，是为了方便外部可以通过xxx.php也可以通过xxx.php来传入类名。同时去掉两端的/。
        $class = str_replace('.php', '', trim($class, '/'));

//因为CI允许通过"dir1/dir2/classname"的格式来组织和加载类，所以还要判断类名中是否包括这些目录信息。
        $subdir = '';
        if (($last_slash = strrpos($class, '/')) !== FALSE)
        {
//目录部分
            $subdir = substr($class, 0, $last_slash + 1);

//类名部分
            $class = substr($class, $last_slash + 1);
        }

//CI允许类文件以大写字母开头或者全小写，下面的遍历，就是在遍历这两种情况。
        foreach (array(ucfirst($class), strtolower($class)) as $class)
        {
            $subclass = APPPATH.'libraries/'.$subdir.config_item('subclass_prefix').$class.'.php';

//是否有我们开发人员自己写的扩展当前类的扩展？如果有的话，把它加载进来。
            if (file_exists($subclass))
            {
//先加载父类。
                $baseclass = BASEPATH.'libraries/'.ucfirst($class).'.php';

                if ( ! file_exists($baseclass))
                {
                    log_message('error', "Unable to load the requested class: ".$class);
                    show_error("Unable to load the requested class: ".$class);
                }

                if (in_array($subclass, $this->_ci_loaded_files))
                {

                    if ( ! is_null($object_name))
                    {
                        $CI =& get_instance();
//我们加载的类最终都是加载给超级控制器的，如果超级控制器已经有的话，那么我们没必要加载。
//如果没有，则实例它并加载给控制器。，
                        if ( ! isset($CI->$object_name))
                        {
                            return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
                        }
                    }

                    $is_duplicate = TRUE;
                    log_message('debug', $class." class already loaded. Second attempt ignored.");
                    return;
                }

//加载类。
                include_once($baseclass);
                include_once($subclass);
//把已加载的类记录到Loader::_ci_loaded_files中。
                $this->_ci_loaded_files[] = $subclass;

//调用Loader::_ci_init_class()方法进而实例化。
                return $this->_ci_init_class($class, config_item('subclass_prefix'), $params, $object_name);
            }

//如果是没有写扩展。方法和上面大致相同，最后都是通过调用Loader::_ci_init_class()方法进而实例化。

            $is_duplicate = FALSE;
            foreach ($this->_ci_library_paths as $path)
            {
                $filepath = $path.'libraries/'.$subdir.$class.'.php';
                if ( ! file_exists($filepath))
                {
                    continue;
                }

                if (in_array($filepath, $this->_ci_loaded_files))
                {
                    if ( ! is_null($object_name))
                    {
                        $CI =& get_instance();
                        if ( ! isset($CI->$object_name))
                        {
                            return $this->_ci_init_class($class, '', $params, $object_name);
                        }
                    }

                    $is_duplicate = TRUE;
                    log_message('debug', $class." class already loaded. Second attempt ignored.");
                    return;
                }

                include_once($filepath);
                $this->_ci_loaded_files[] = $filepath;
                return $this->_ci_init_class($class, '', $params, $object_name);
            }

        } // END FOREACH

//其实正常的话，上面如果找到此类就找到，没找到就没有了。不过CI在会这里做最后的尝试。会不会是放到了一个同名的
//子目录下。
        if ($subdir == '')
        {
            $path = strtolower($class).'/'.$class;
            return $this->_ci_load_class($path, $params);
        }

//没有找到就报错咯。
        if ($is_duplicate == FALSE)
        {
            log_message('error', "Unable to load the requested class: ".$class);
            show_error("Unable to load the requested class: ".$class);
        }
    }

// --------------------------------------------------------------------

//此方法是用于实例化已经把类文件include进来的类。
    protected function _ci_init_class($class, $prefix = '', $config = FALSE, $object_name = NULL)
    {
// Is there an associated config file for this class? Note: these should always be lowercase
        if ($config === NULL)
        {
// Fetch the config paths containing any package paths
            $config_component = $this->_ci_get_component('config');

            if (is_array($config_component->_config_paths))
            {
// Break on the first found file, thus package files
// are not overridden by default paths
                foreach ($config_component->_config_paths as $path)
                {
// We test for both uppercase and lowercase, for servers that
// are case-sensitive with regard to file names. Check for environment
// first, global next
                    if (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php'))
                    {
                        include($path .'config/'.ENVIRONMENT.'/'.strtolower($class).'.php');
                        break;
                    }
                    elseif (defined('ENVIRONMENT') AND file_exists($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php'))
                    {
                        include($path .'config/'.ENVIRONMENT.'/'.ucfirst(strtolower($class)).'.php');
                        break;
                    }
                    elseif (file_exists($path .'config/'.strtolower($class).'.php'))
                    {
                        include($path .'config/'.strtolower($class).'.php');
                        break;
                    }
                    elseif (file_exists($path .'config/'.ucfirst(strtolower($class)).'.php'))
                    {
                        include($path .'config/'.ucfirst(strtolower($class)).'.php');
                        break;
                    }
                }
            }
        }

        if ($prefix == '')
        {
            if (class_exists('CI_'.$class))
            {
                $name = 'CI_'.$class;
            }
            elseif (class_exists(config_item('subclass_prefix').$class))
            {
                $name = config_item('subclass_prefix').$class;
            }
            else
            {
                $name = $class;
            }
        }
        else
        {
            $name = $prefix.$class;
        }

// Is the class name valid?
        if ( ! class_exists($name))
        {
            log_message('error', "Non-existent class: ".$name);
            show_error("Non-existent class: ".$class);
        }

// Set the variable name we will assign the class to
// Was a custom class name supplied? If so we'll use it
        $class = strtolower($class);

        if (is_null($object_name))
        {
            $classvar = ( ! isset($this->_ci_varmap[$class])) ? $class : $this->_ci_varmap[$class];
        }
        else
        {
            $classvar = $object_name;
        }

// Save the class name and object name
        $this->_ci_classes[$class] = $classvar;

// Instantiate the class
        $CI =& get_instance();
        if ($config !== NULL)
        {
            $CI->$classvar = new $name($config);
        }
        else
        {
            $CI->$classvar = new $name;
        }
    }

// --------------------------------------------------------------------

    /**
     * Autoloader
     *
     * The config/autoload.php file contains an array that permits sub-systems,
     * libraries, and helpers to be loaded automatically.
     *
     */
    private function _ci_autoloader()
    {
        if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/autoload.php'))
        {
            include(APPPATH.'config/'.ENVIRONMENT.'/autoload.php');
        }
        else
        {
            include(APPPATH.'config/autoload.php');
        }

        if ( ! isset($autoload))
        {
            return FALSE;
        }

// Autoload packages
        if (isset($autoload['packages']))
        {
            foreach ($autoload['packages'] as $package_path)
            {
                $this->add_package_path($package_path);
            }
        }

// Load any custom config file
        if (count($autoload['config']) > 0)
        {
            $CI =& get_instance();
            foreach ($autoload['config'] as $key => $val)
            {
                $CI->config->load($val);
            }
        }

// Autoload helpers and languages
        foreach (array('helper', 'language') as $type)
        {
            if (isset($autoload[$type]) AND count($autoload[$type]) > 0)
            {
                $this->$type($autoload[$type]);
            }
        }

// A little tweak to remain backward compatible
// The $autoload['core'] item was deprecated
        if ( ! isset($autoload['libraries']) AND isset($autoload['core']))
        {
            $autoload['libraries'] = $autoload['core'];
        }

// Load libraries
        if (isset($autoload['libraries']) AND count($autoload['libraries']) > 0)
        {
// Load the database driver.
            if (in_array('database', $autoload['libraries']))
            {
                $this->database();
                $autoload['libraries'] = array_diff($autoload['libraries'], array('database'));
            }

// Load all other libraries
            foreach ($autoload['libraries'] as $item)
            {
                $this->library($item);
            }
        }

// Autoload models
        if (isset($autoload['model']))
        {
            $this->model($autoload['model']);
        }
    }

// --------------------------------------------------------------------

    /**
     * Object to Array
     *
     * Takes an object as input and converts the class variables to array key/vals
     *
     */
    protected function _ci_object_to_array($object)
    {
        return (is_object($object)) ? get_object_vars($object) : $object;
    }

// --------------------------------------------------------------------

    /**
     * Get a reference to a specific library or model
     *
     */
    protected function &_ci_get_component($component)
    {
        $CI =& get_instance();
        return $CI->$component;
    }

// --------------------------------------------------------------------

    /**
     * Prep filename
     *
     * This function preps the name of various items to make loading them more reliable.
     *
     */
    protected function _ci_prep_filename($filename, $extension)
    {
        if ( ! is_array($filename))
        {
            return array(strtolower(str_replace('.php', '', str_replace($extension, '', $filename)).$extension));
        }
        else
        {
            foreach ($filename as $key => $val)
            {
                $filename[$key] = strtolower(str_replace('.php', '', str_replace($extension, '', $val)).$extension);
            }

            return $filename;
        }
    }
}